#include "ActionApiMatchSettlement.h"
#include "json.h"
#include "util.h"
#include "streamdata.h"
#include "agency_event_service.h"
#include "agencyeventmgr.h"
#include "agencyevent.h"
#include "3400_api_order.pb.h"
#include "api_order_service.h"
#include "flow_service.h"
#include "9200_agency_event_api.pb.h"
#include "public_define.h"


static std::map<std::string,std::string> http_json_heads = {{"Content-Type","application/json"}};

static bool __init__ = ActionApiMatchSettlement::Init();

bool ActionApiMatchSettlement::Init()
{
    Ctx.msg_router.BindMsg(agency_event_api::AGENCY_EVENT_API_MSGID_MATCH_SETTLEMENT_REQ, ActionApiMatchSettlement::HandleMatchSettlement);
    AgencyEventService::Instance()->ListenOnAgencyEventGet(AgencyEventService::MATCHSETTLEMENT, ActionApiMatchSettlement::OnAgencyGet);

    return true;
}

void ActionApiMatchSettlement::HandleMatchSettlement(MsgData&& msg_data)
{
    agency_event_api::MatchSettlementReq req;
    agency_event_api::MatchSettlementResp resp;

    do {
        if (!req.ParseFromArray(msg_data.data.data(), msg_data.data.size()))
        {
            resp.set_errorcode(agency_event_api::ErrorCode::DataParseErr);
            log_error("parser msg failed");
            break;
        }
        if (req.fee() < 0) req.set_fee(-req.fee());
        if (req.reward() < 0) req.set_reward(-req.reward());

        log_debug("req:%s", req.ShortDebugString().c_str());

        if (req.matchid() <= 0)
        {
            resp.set_errorcode(agency_event_api::ErrorCode::InvaildParam);
            log_error("matchid is invalid, req:%s", req.ShortDebugString().c_str());
            break;
        }

        if (req.matchtime() <= 0)
        {
            resp.set_errorcode(agency_event_api::ErrorCode::InvaildParam);
            log_error("matchtime is invalid, req:%s", req.ShortDebugString().c_str());
            break;
        }

        stAgencyEvent* agency = AgencyEventMgr::Instance()->Get(req.agency_id());
        if (agency != nullptr)
        {
            MatchSettlementParam params;
            params.currency_unit_multi = agency->currency_unit_multi;
            params.balance = req.balance();
            params.withdraw_limit = req.withdraw_limit();
            params.valid_flow = req.valid_flow();
            params.agency_id = agency->agency_id;
            params.order_id = req.order_id();
            params.game = req.game();
            params.matchid = req.matchid();
            params.fee = req.fee();
            params.reward = req.reward();
            params.matchtime = req.matchtime();
            params.matchtype = req.matchtype();
            params.withdraw_limit_ratio = req.withdraw_limit_ratio();
            params.matchform = req.matchform();
            params.totalfee = req.totalfee();
        
            params.agency = agency->agency_name;
            params.currency = agency->currency;
            params.username = req.username();
        

            Request(agency->call_url, req.trans(), 
                req.uid(), msg_data.svtype, msg_data.svid, msg_data.uuid, req.scene_from(), 
                params);
        }
        else
        {
            StreamData stream_data;
            StreamDataWriter writer(stream_data);
            writer.WriteShort(msg_data.svtype);
            writer.WriteInt(msg_data.svid);
            writer.WriteInt(msg_data.uuid);
            writer.WriteInt(req.scene_from());
            writer.WriteInt(req.uid());
            writer.WriteInt(req.game());
            writer.WriteInt64(req.matchid());
            writer.WriteInt64(req.order_id());
            writer.WriteString(req.trans());
            writer.WriteString(req.username());
            writer.WriteInt64(req.fee());
            writer.WriteInt64(req.reward());
            writer.WriteInt64(req.matchtime());
            writer.WriteInt(req.matchtype());
            writer.WriteInt64(req.balance());
            writer.WriteInt64(req.withdraw_limit());
            writer.WriteInt64(req.valid_flow());
            writer.WriteInt64(req.withdraw_limit_ratio());
            writer.WriteInt(req.matchform());
            writer.WriteInt64(req.totalfee());

            if (!AgencyEventService::Instance()->HandleGetAgencyEvent(req.agency_id(), std::move(stream_data.Data()), AgencyEventService::MATCHSETTLEMENT))
            {
                resp.set_errorcode(agency_event_api::ErrorCode::AgentGetErr);
                log_error("parser msg failed");
                break;
            }
        }
        return;
    } while(false);

    if (resp.errorcode() != 0)
    {
        // 回包
        resp.set_uid(req.uid());
        resp.set_trans(req.trans());
        resp.set_message(ToString(resp.errorcode()));
        Ctx.host->MtSendtoOneSrv(agency_event_api::AGENCY_EVENT_API_MSGID_MATCH_SETTLEMENT_RESP, &resp, msg_data.svtype, msg_data.svid, false, msg_data.uuid);
    }
}

void ActionApiMatchSettlement::OnAgencyGet(std::int32_t err_code, stAgencyEvent* agency, std::string&& trans)
{
    StreamData stream_data(std::move(trans));
    StreamDataReader reader(stream_data);

    MatchSettlementParam params;
    std::uint16_t svtype = reader.ReadShort();
    std::uint32_t svid = reader.ReadInt();
    std::uint32_t uuid = reader.ReadInt();
    std::uint32_t scene_from = reader.ReadInt();
    std::uint32_t uid = reader.ReadInt();
    params.game = reader.ReadInt();
    params.matchid = reader.ReadInt64();
    params.order_id = reader.ReadInt64();
    trans = reader.ReadString();
    params.username = reader.ReadString();
    params.fee = reader.ReadInt64();
    params.reward  = reader.ReadInt64();
    params.matchtime  = reader.ReadInt64();
    params.matchtype = reader.ReadInt();
    params.balance = reader.ReadInt64();
    params.withdraw_limit = reader.ReadInt64();
    params.valid_flow = reader.ReadInt64();
    params.withdraw_limit_ratio = reader.ReadInt64();
    params.matchform = reader.ReadInt();
    params.totalfee = reader.ReadInt64();

    agency_event_api::MatchSettlementResp resp;
    do
    {
        if (err_code != 0)
        {
            resp.set_errorcode(agency_event_api::ErrorCode::AgencyGetErr);
            log_error("get agencyevent info failed,uid:%u,err_code:%d", uid, err_code);
            break;
        }
        if (agency == nullptr)
        {
            resp.set_errorcode(agency_event_api::ErrorCode::AgencyGetErr);
            log_error("agencyevent is nullptr,uid:%u,err_code:%d", uid, err_code);
            break;
        }

		log_debug("agency:%s", agency->ToString().c_str());

        params.agency = agency->agency_name;
        params.currency = agency->currency;
        params.currency_unit_multi = agency->currency_unit_multi;
        params.agency_id = agency->agency_id;

        Request(agency->call_url, trans, 
            uid, svtype, svid, uuid, scene_from,
            params);
        return;
    } while(false);

    if (resp.errorcode() != 0)
    {
        // 回包
        resp.set_uid(uid);
        resp.set_trans(std::move(trans));
        resp.set_message(ToString(resp.errorcode()));
        Ctx.host->MtSendtoOneSrv(agency_event_api::AGENCY_EVENT_API_MSGID_MATCH_SETTLEMENT_RESP, &resp, svtype, svid, false, uuid);
    }
}

void ActionApiMatchSettlement::Request(const std::string& baseurl, const std::string& trans,
    std::uint32_t uid, std::uint16_t svtype, std::uint32_t svid, std::uint32_t uuid, std::uint32_t scene_from,
    const MatchSettlementParam& params)
{
    agency_event_api::MatchSettlementResp resp;
    do
    {
        std::string req_id = GenTransId();
        std::uint64_t now = CTools::GetUnixTime();

        if (params.currency_unit_multi == 0)
        {
            resp.set_errorcode(agency_event_api::ErrorCode::AbnormalErr);
            log_error("currency_unit_multi is 0, uid:%u ,matchid:%lld,order_id:%lld,currency:%s", uid, params.matchid, params.order_id, params.currency.c_str());
            break;
        }

        Json::Value http_req;
        http_req["reqId"] = req_id;
        http_req["username"] = params.username;
        http_req["currency"] = params.currency;
        http_req["game"] = params.game;
        http_req["round"] = Json::UInt64(params.order_id);
        http_req["sessionId"] = Json::UInt64(params.matchid);
        http_req["fee"] = params.fee * 1.0 / params.currency_unit_multi;
        http_req["reward"] = params.reward * 1.0 / params.currency_unit_multi;
        http_req["matchtime"] = Json::UInt64(params.matchtime);
        http_req["matchtype"] = params.matchtype;
        http_req["withdrawlimit"] = params.withdraw_limit * 1.0 / params.currency_unit_multi;
        http_req["validflow"] = params.valid_flow * 1.0 / params.currency_unit_multi;
        http_req["withdrawlimitratio"] = params.withdraw_limit_ratio * 1.0 / RATIO_BASE;
        http_req["matchform"] = params.matchform;
        http_req["totalfee"] = params.totalfee * 1.0 / params.currency_unit_multi;
        
        http_req["wagersTime"] = Json::UInt64(now);

        std::string json_string = CTools::ToStr(http_req);
        std::string url = baseurl + "/matchSettlement";

        api_order::ApiOrder api_order;
        api_order.set_scene_from(scene_from);
        api_order.set_stype_from(svtype);
        api_order.set_svid_from(svid);
        api_order.set_uid(uid);
        api_order.set_username(params.username);
        api_order.set_agency(params.agency);
        api_order.set_currency(params.currency);
        api_order.set_gameid(params.game);
        api_order.set_op(api_order::API_ORDER_OP_MATCHSETTLEMENT);
        api_order.set_url(url);
        api_order.set_req_id(req_id);
        api_order.set_req_body(json_string);
        api_order.set_start_time(now);
        api_order.set_update_time(now);
        api_order.set_cycle(params.matchid);
        api_order.set_orderid(params.order_id);
        api_order.set_balance(params.balance);
        api_order.set_bet(params.reward);

        std::uint32_t event_id = 0;
        // 请求http网络
        if (!Ctx.msg_router.sendHttpPOST(url, json_string, http_json_heads, event_id) || (event_id == 0))
        {
            resp.set_errorcode(agency_event_api::ErrorCode::SendPostErr);
            log_error("net post failed,url:%s,param:%s", url.c_str(),json_string.c_str());
            break;
        }
        log_debug("http post,url:%s,param:%s", url.c_str(), json_string.c_str());
        // 添加回调监听
        Ctx.msg_router.AddCurlEvent(event_id, [uid, svtype, svid, uuid, trans=std::move(trans), params = params,
            url=std::move(url), json_string= std::move(json_string), api_order_v = std::move(api_order)](std::string& body, bool is_ok){
            agency_event_api::MatchSettlementResp resp;
            Json::Value http_resp;
            
            std::uint64_t now = CTools::GetUnixTime();

            api_order::ApiOrder api_order = std::move(api_order_v);
            api_order.set_update_time(now);
            api_order.set_resp_body(body);
            do
            {
                // 网络错误
                if (!is_ok)
                {
                    resp.set_errorcode(agency_event_api::ErrorCode::RespTimeout);
                    log_error("time out,url:%s,param:%s", url.c_str(), json_string.c_str());
                    break;
                }
                
                // 解析返回格式
                if (!CTools::FromStr(http_resp, body))
                {
                    // resp.set_errorcode(agency_event_api::ErrorCode::DataParseErr);
                    resp.set_errorcode(agency_event_api::ErrorCode::RespTimeout);
                    log_error("resp from json failed, body:%s ,url:%s,param:%s", body.c_str(), url.c_str(), json_string.c_str());
                    break;
                }

                if (!http_resp.isMember("currency") || !http_resp["currency"].isString()
                    || !http_resp.isMember("balance") || !http_resp["balance"].isDouble()
                    || !http_resp.isMember("errorCode") || !http_resp["errorCode"].isInt()
                    )
                {
                    resp.set_errorcode(agency_event_api::ErrorCode::InvaildParam);
                    log_error("response param invalid, body:%s ,url:%s,param:%s", body.c_str(), url.c_str(), json_string.c_str());
                    break;
                }
            
                if (params.currency_unit_multi == 0)
                {
                    resp.set_errorcode(agency_event_api::ErrorCode::AbnormalErr);
                    log_error("currency_unit_multi is 0, body:%s ,url:%s,param:%s", body.c_str(), url.c_str(), json_string.c_str());
                    break;
                }

                resp.set_errorcode(http_resp["errorCode"].asInt());
                resp.set_balance(http_resp["balance"].asDouble() * params.currency_unit_multi);
                std::int64_t txid = 0;
                if (http_resp.isMember("txId") && http_resp["txId"].isInt64()) 
                {
                    txid = http_resp["txId"].asInt64();
                }
                
                api_order.set_resp_id(txid);
                api_order.set_balance(resp.balance());
            } while (false);
            
            // 回包
            resp.set_uid(uid);
            resp.set_trans(std::move(trans));
            resp.set_message(ToString(resp.errorcode()));

            log_debug("resp:%s", resp.ShortDebugString().c_str());

            Ctx.host->MtSendtoOneSrv(agency_event_api::AGENCY_EVENT_API_MSGID_MATCH_SETTLEMENT_RESP, &resp, svtype, svid, false, uuid);
            
            // 上报流水
            if (api_order.scene_from() == 0)
            {
                FlowService::Instance()->OnMatchSettlement(resp.errorcode(), uid, GameProvider(), params.agency_id, params.game, params.withdraw_limit, params.valid_flow,
                    params.matchid, params.order_id, api_order.start_time(), resp.balance(), params.fee, params.matchtime, params.matchtype, params.reward,
                    params.withdraw_limit_ratio);
            }

            // 订单更新
            api_order.set_err_code(resp.errorcode());
            ApiOrderService::Instance()->OnApiOrderUpdate(api_order);
        });

        return;
    } while(false);

    if (resp.errorcode() != 0)
    {
        // 回包
        resp.set_uid(uid);
        resp.set_trans(std::move(trans));
        resp.set_message(ToString(resp.errorcode()));
        Ctx.host->MtSendtoOneSrv(agency_event_api::AGENCY_EVENT_API_MSGID_MATCH_SETTLEMENT_RESP, &resp, svtype, svid, false, uuid);
    }
}
